public class Docker笔记 {
/*
Docker笔记

为什么需要docker?

微服务虽然具备各种各样的优势，但服务的拆分通用给部署带来了很大的麻烦。

- 分布式系统中，依赖的组件非常多，不同组件之间部署时往往会产生一些冲突。
- 在数百上千台服务中重复部署，环境不一定一致，会遇到各种问题

例如一个项目中，部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等，这些服务部署时所需要的函数库、依赖项各不相同，甚至会有冲突。给部署带来了极大的困难。



Docker为了解决依赖的兼容问题的，采用了两个手段：

- 将应用的Libs（函数库）、Deps（依赖）、配置与应用一起打包
- 将每个应用放到一个隔离**容器**去运行，避免互相干扰

这样打包好的应用包中，既包含应用本身，也保护应用所需要的Libs、Deps，无需再操作系统上安装这些，自然就不存在不同应用之间的兼容问题了。




应用与计算机交互的流程如下：

1）应用调用操作系统应用（函数库），实现各种功能

2）系统函数库是对内核指令集的封装，会调用内核指令

3）内核指令操作计算机硬件



Docker如何解决不同系统环境的问题？
- Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包
- Docker运行到不同操作系统时，直接基于打包的函数库，借助于操作系统的Linux内核来运行




Docker如何解决大型项目依赖关系复杂，不同组件依赖的兼容性问题？
- Docker允许开发中将应用、依赖、函数库、配置一起**打包**，形成可移植镜像
- Docker应用运行在容器中，使用沙箱机制，相互**隔离**

Docker如何解决开发、测试、生产环境有差异的问题？
- Docker镜像中包含完整运行环境，包括系统函数库，仅依赖系统的Linux内核，因此可以在任意Linux操作系统上运行


Docker是一个快速交付应用、运行应用的技术，具备下列优势：

- 可以将程序及其依赖、运行环境一起打包为一个镜像，可以迁移到任意Linux操作系统
- 运行时利用沙箱机制形成隔离容器，各个应用互不干扰
- 启动、移除都可以通过一行命令完成，方便快捷



Docker和虚拟机的差异：

- docker是一个系统进程；虚拟机是在操作系统中的操作系统
- docker体积小、启动速度快、性能好；虚拟机体积大、启动速度慢、性能一般



Docker中有几个重要的概念：

**镜像（Image）**：Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起，称为镜像,镜像是只读的,不能更改。
**容器（Container）**：镜像中的应用程序运行后形成的进程就是**容器**，只是Docker会给容器进程做隔离，对外不可见。
而**镜像**，就是把一个应用在硬盘上的文件、及其运行环境、部分系统函数库文件一起打包形成的文件包。这个文件包是只读的。
**容器**呢，就是将这些文件中编写的程序、函数加载到内存中运行，形成进程，只不过要隔离起来。因此一个镜像可以启动多次，形成多个容器进程。
例如你下载了一个QQ，如果我们将QQ在磁盘上的运行**文件**及其运行的操作系统依赖打包，形成QQ镜像。然后你可以启动多次，双开、甚至三开QQ，跟多个妹子聊天。


- DockerHub：DockerHub是一个官方的Docker镜像的托管平台。这样的平台称为Docker Registry。
- 国内也有类似于DockerHub 的公开服务，比如 [网易云镜像服务](https://c.163yun.com/hub)、[阿里云镜像库](https://cr.console.aliyun.com/)等。


我们要使用Docker来操作镜像、容器，就必须要安装Docker。
Docker是一个CS架构的程序，由两部分组成：
- 服务端(server)：Docker守护进程，负责处理Docker指令，管理镜像、容器等
- 客户端(client)：通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。



CentOS安装Docker
Docker CE 支持 64 位版本 CentOS 7，并且要求内核版本不低于 3.10， CentOS 7 满足最低内核的要求，所以我们在CentOS 7安装Docker。


镜像的名称组成：
- 镜名称一般分两部分组成：[repository]:[tag]。  mysql:5.7这里的mysql就是repository，5.7就是tag，合一起就是镜像名称，代表5.7版本的MySQL镜像。
- 在没有指定tag时，默认是latest，代表最新版本的镜像



docker镜像操作指令:
docker images
利用docker pull命令拉取镜像
利用docker save命令将 redis:latest打包为一个redis.tar包
利用docker rmi 删除本地的redis:latest
利用docker load 重新加载 redis.tar文件
docker push


docker容器操作指令:
docker run --name containerName -p 80:80 -d nginx
需要去docker hub查看具体某个镜像的容器运行命令
命令解读：
docker run ：创建并运行一个容器
--name : 给容器起一个名字，比如叫做mn
-p ：将宿主机端口与容器端口映射，冒号左侧是宿主机端口，右侧是容器端口
-d：后台运行容器
nginx：镜像名称，例如nginx (不指定版本默认最新)

查看容器日志的命令：
docker logs
添加 -f 参数可以持续查看日志

查看容器状态：
docker ps
添加-a参数查看所有状态的容器

-------------------------------------
优化docker ps视觉效果
vim ~/.bashrc

#增加如下内容
alias dps='docker ps --format "table{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}"'
#保存退出

#生效
source ~/.bashrc
此后直接使用 dps 即可
-------------------------------------

docker pause 容器名 暂停
docker unpause 容器名 恢复
docker stop 容器名 停止
docker start 容器名 重新执行已停止的容器
docker restart 容器名 停止+重新执行
docker logs -f 容器名 查看容器日志 -f霸屏

进入容器：
命令是docker exec -it [容器名] [要执行的命令]
exec命令可以进入容器修改文件，但是在容器内修改文件是不推荐的
docker exec -it mn bash
docker exec -it mr redis-cli( = docker exec -it mn bash + redis-cli )
docker exec ：进入容器内部，执行一个命令
-it : 给当前进入的容器创建一个标准输入、输出终端，允许我们与容器交互
mn ：要进入的容器的名称
bash：进入容器后执行的命令，bash是一个linux终端交互命令

删除容器：
docker rm
不能删除运行中的容器，除非添加 -f 参数


创建并运行一个redis容器，并且支持数据持久化
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes
步骤一：进入redis容器
docker exec -it redis bash
步骤二：执行redis-cli客户端命令
redis-cli
步骤三：设置数据num=666
set num 666

exit退出容器


数据卷（volume）是一个虚拟目录，指向宿主机文件系统中的某个目录。
DockerHost下面的volumes 对应 宿主机的/var/lib/docker/volumes/目录
一旦完成数据卷挂载，对容器的一切操作都会作用在数据卷对应的宿主机目录了。
这样，我们操作宿主机的/var/lib/docker/volumes/html目录，就等于操作容器内的/usr/share/nginx/html目录了
也就是说容器内的目录通过数据卷与宿主机的目录直接映射了,解决了容器内数据修改不方便的问题,对宿主机中文件的修改会立即反应到与数据卷关联的容器内部文件
多个容器可以挂载同意个数据卷,新的容器可以看到原来数据卷的一切修改
将来删除容器时,数据还在数据卷中,宿主机对应目录也还在,新的容器直接挂载到数据卷即可获取之前的数据
因此数据卷解决了容器与数据（容器内文件）耦合带来的问题 (不便于修改,数据不可复用,升级维护困难),方便操作容器内数据,保证数据安全


数据卷操作的基本语法如下：
docker volume [COMMAND]
docker volume命令是数据卷操作，根据命令后跟随的command来确定下一步的操作：
 create	创建一个volume
 inspect	显示一个或多个volume的信息
 ls		列出所有的volume
 prune		删除未使用的volume
 rm		删除一个或多个指定的volume

数据卷操作：
docker volume create
docker volume ls
docker volume inspect
docker volume rm
docker volume prune


### 挂载数据卷
我们在创建容器时，可以通过 -v 参数来挂载一个数据卷到某个容器内目录，命令格式如下：
docker run \
  --name mn \
  -v html:/root/html \
  -p 8080:80
  nginx \
这里的-v就是挂载数据卷的命令：
-v html:/root/html ：把html数据卷挂载到容器内的/root/html这个目录中

创建nginx容器并挂载数据卷到容器内的HTML目录
docker run --name mn -v html:/usr/share/nginx/html -p 80:80 -d nginx
如果创建容器挂载数据卷时还没有创建数据卷,会一并创建容器和创建数据卷并挂载

docker volume inspect html
可以看到，我们创建的html这个数据卷关联的宿主机目录为/var/lib/docker/volumes/html/_data目录。
[root@wtp _data]#  cd /var/lib/docker/volumes/html/_data
[root@wtp _data]# ls
50x.html  index.html
这两个文件与容器内/usr/share/nginx/html下文件相同,实时同步


容器不仅仅可以挂载数据卷，也可以直接挂载到宿主机目录上。关联关系如下：
- 带数据卷模式：宿主机目录 --> 数据卷 ---> 容器内目录
- 直接挂载模式：宿主机目录 ---> 容器内目录
目录挂载与数据卷挂载的语法是类似的：
 -v [宿主机目录]:[容器内目录]
 -v [宿主机文件]:[容器内文件]

 数据卷挂载与目录直接挂载的区别
- 数据卷挂载耦合度低，由docker全自动创建宿主机目录来映射挂载数据卷时指定的容器目录，无需使用者操心,但是宿主机目录较深，不好找
- 目录/文件挂载耦合度高，需要我们自己管理目录，不过目录容易寻找查看




**需求**：创建并运行一个MySQL容器，将宿主机目录直接挂载到容器
实现思路如下：
1）在将课前资料中的mysql.tar文件上传到虚拟机，通过load命令加载为镜像
2）创建目录/tmp/mysql/data
3）创建目录/tmp/mysql/conf，将课前资料提供的hmy.cnf文件上传到/tmp/mysql/conf
4）去DockerHub查阅资料，创建并运行MySQL容器，要求：
① 挂载/tmp/mysql/data到mysql容器内数据存储目录
② 挂载/tmp/mysql/conf/hmy.cnf到mysql容器的配置文件
③ 设置MySQL密码
docker run \
--name mysql \
-e MYSQL_ROOT_PASSWORD=123 \
-d \
-p 3306:3306 \
-v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
-v /tmp/mysql/data:/var/lib/mysql \
mysql:5.7.25




镜像是分层结构，每一层称为一个Layer
BaseImage层：包含基本的系统函数库、环境变量、文件系统
Entrypoint：入口，是镜像中应用启动的命令
其它：在BaseImage基础上添加依赖、安装程序、完成整个应用的安装和配置



自定义镜像
什么是Dockerfile
Dockerfile就是一个文本文件，其中包含一个个的指令(Instruction)，用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。

指令      	说明                                      	示例
FROM    	指定基础镜像                              	FROM centos:6
ENV     	设置环境变量，可在后面指令使用             	ENV key value
COPY    	拷贝本地文件到镜像的指定目录              	COPY ./mysql-5.7.rpm /tmp
RUN     	执行Linux的shell命令，一般是安装过程的命令	RUN yum install gcc
EXPOSE  	指定容器运行时监听的端口，是给镜像使用者看的  EXPOSE 8080
ENTRYPOINT	镜像中应用的启动命令，容器运行时调用	ENTRYPOINT java -jar xx.jar

更新详细语法说明，请参考官网文档： https://docs.docker.com/engine/reference/builder


需求：基于Ubuntu镜像构建一个新镜像，运行一个java项目

步骤1：新建一个空文件夹docker-demo
步骤2：拷贝课前资料中的docker-demo.jar文件到docker-demo这个目录
步骤3：拷贝课前资料中的jdk8.tar.gz文件到docker-demo这个目录
步骤4：拷贝课前资料提供的Dockerfile到docker-demo这个目录

其中的内容如下：
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量，JDK的安装目录
ENV JAVA_DIR=/usr/local

# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar

# 安装JDK
RUN cd $JAVA_DIR \
 && tar -xf ./jdk8.tar.gz \
 && mv ./jdk1.8.0_144 ./java8

# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin

# 暴露端口
EXPOSE 8090
# 入口，java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar

- 步骤5：进入docker-demo
  将准备好的docker-demo上传到虚拟机任意目录，然后进入docker-demo目录下

- 步骤6：运行命令：

docker build -t javaweb:1.0 .  (. 代表Dockerfile所在的目录)

最后访问 http://192.168.150.101:8090/hello/count，其中的ip改成你的虚拟机ip

小结:
1.Dockerfile的本质是一个文件，通过指令描述镜像的构建过程
2.Dockerfile的第一行必须是FROM，从一个基础镜像来构建
3.基础镜像可以是基本操作系统，如Ubuntu。也可以是其他人制作好的镜像，例如：java:8-alpine



Docker Compose可以基于Compose文件帮我们快速的部署分布式应用，而无需手动一个个创建和运行容器！
Compose文件是一个文本文件，通过指令定义集群中的每个容器如何运行。格式如下(yml的格式)：

version: "3.8"
 services:
  mysql:
      image: mysql:5.7.25
    environment:
      MYSQL_ROOT_PASSWORD: 123
    volumes:
     - "/tmp/mysql/data:/var/lib/mysql"
     - "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
  web:
    build: .
    ports:
     - "8090:8090"

上面的Compose文件就描述一个项目，其中包含两个容器：
- mysql：一个基于`mysql:5.7.25`镜像构建的容器，并且挂载了两个目录
- web：一个基于Dockerfile临时构建的镜像容器，映射端口时8090
DockerCompose的详细语法参考官网：https://docs.docker.com/compose/compose-file/

DockerCompose部署的微服务集群中,多个服务之间用服务名进行相互访问
因为微服务将来要部署为docker容器，而容器之间互联不是通过IP地址，而是通过容器名。这里我们将order-service、user-service、gateway服务的mysql、nacos地址都修改为基于容器名的访问。
spring:
  datasource:
    url: jdbc:mysql://mysql:3306/cloud_order?useSSL=false
    username: root
    password: 123
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice
  cloud:
    nacos:
      server-addr: nacos:8848     # <--- nacos服务地址

相关命令
docker-compose up -d #创建执行容器
docker-compose logs -f #查看日志





Docker镜像仓库

镜像仓库（ Docker Registry ）有公共的和私有的两种形式：
公共仓库：例如Docker官方的 Docker Hub，国内也有一些云服务商提供类似于 Docker Hub 的公开服务，比如 网易云镜像服务、DaoCloud 镜像服务、阿里云镜像服务等。
除了使用公开仓库外，用户还可以在本地搭建私有 Docker Registry。企业自己的镜像最好是采用私有Docker Registry来实现。



推送镜像到私有镜像服务必须先tag，步骤如下：

① 重新tag本地镜像，名称前缀为私有仓库的地址：192.168.150.101:8080/
docker tag nginx:latest 192.168.150.101:8080/nginx:1.0
② 推送镜像
docker push 192.168.150.101:8080/nginx:1.0
③ 拉取镜像
docker pull 192.168.150.101:8080/nginx:1.0

小结:
推送本地镜像到仓库前都必须重命名(docker tag)镜像，以镜像仓库地址为前缀
镜像仓库推送前需要把仓库地址配置到docker服务的daemon.json文件中，被docker信任
推送使用docker push命令
拉取使用docker pull命令



*/


}
